﻿Imports System.IO
Imports System.Threading.Tasks
Imports System.Text
Imports System.Linq
Imports System.ComponentModel
Imports Runa.Common.Helper
Imports ICSharpCode.SharpZipLib.Zip
Imports ICSharpCode.SharpZipLib.Zip.Compression

Module Helper

    Public Function GenericCast(Of U, V)(ByVal obj As U) As V
        Try
            Return CType(DirectCast(obj, Object), V)
        Catch ex As InvalidCastException
            Return DirectCast(CompilerServices.Conversions.ChangeType(obj, GetType(V)), V)
        End Try
    End Function

    Public Function GetStreamMd5Hash(ByVal inputStream As System.IO.Stream) As String
        Dim strResult As String = ""
        Dim strHashData As String = ""
        Dim arrbytHashValue As Byte()
        Dim MD5Hasher As New System.Security.Cryptography.MD5CryptoServiceProvider()
        Try
            arrbytHashValue = MD5Hasher.ComputeHash(inputStream)
            inputStream.Close()
            strHashData = System.BitConverter.ToString(arrbytHashValue)
            strHashData = strHashData.Replace("-", "")
            strResult = strHashData
        Catch ex As System.Exception
            Throw ex
        End Try
        Return strResult
    End Function

    Public Function GetDateString(ByVal dt As Date) As String
        If dt.Hour >= 0 And dt.Hour < 6 Then
            Return Format(dt, "yyyyMMdd")
        Else
            Return Format(dt.AddDays(1), "yyyyMMdd")
        End If
    End Function

    Public Function GetCurrentDateString() As String
        Return GetDateString(Now)
    End Function

    Public Function GetNomalizedPath(ByVal p As String) As String
        Dim replace As New System.Text.RegularExpressions.Regex("(/|\\)+")
        Return replace.Replace(p, "/")
    End Function

    Public Function GetRelativePath(ByVal sourcePath As String, ByVal targetBase As String) As String
        Dim replace As New System.Text.RegularExpressions.Regex("(/|\\)+")
        targetBase = replace.Replace(Path.GetFullPath(targetBase), "/")
        sourcePath = replace.Replace(Path.GetFullPath(sourcePath), "/")
        If sourcePath.EndsWith("/") = False Then
            sourcePath = sourcePath.Substring(0, sourcePath.LastIndexOf("/"c) + 1)
        End If
        Dim p As String() = sourcePath.Split(New Char() {"/"c}, StringSplitOptions.RemoveEmptyEntries)
        Dim t As String() = targetBase.Split(New Char() {"/"c}, StringSplitOptions.RemoveEmptyEntries)
        Dim len As Integer = Math.Min(p.Length, t.Length)
        Dim same As Integer = 0
        While same < len
            If p(same) <> t(same) Then
                Exit While
            End If
            same += 1
        End While
        If same = 0 Then
            Return targetBase
        End If
        Dim sb As New StringBuilder("./")
        For i As Integer = same To p.Length - 1
            sb.Append("../")
        Next
        For i As Integer = same To t.Length - 1
            sb.Append("/"c).Append(t(i))
        Next
        If targetBase.EndsWith("/") Then
            sb.Insert(2, "../")
            sb.Append("/"c)
        End If
        Return replace.Replace(sb.ToString(), "/")
    End Function

    Public Sub CheckFilesInDirectory(ByVal sourcePath As String, ByVal relativePath As String, ByVal processor As Action(Of FileInfo), ByVal callback As AsyncCallback, ByVal ParamArray exts() As String)
        Try
            Dim dirSourceCurrent As New DirectoryInfo(System.IO.Path.Combine(sourcePath, relativePath))
            Dim sourceDirs = New List(Of DirectoryInfo)(dirSourceCurrent.GetDirectories())
            Dim sourceFiles As New List(Of FileInfo)(dirSourceCurrent.GetFiles())
            Parallel.ForEach(sourceFiles,
                Sub(sourceItem, state)
                    If exts.Length > 0 Then
                        Dim ext = System.IO.Path.GetExtension(sourceItem.FullName).ToLower()
                        If exts.Contains(Function(obj) obj = ext) Then
                            Dim asyncResult = processor.BeginInvoke(sourceItem, callback, processor)
                        End If
                    Else
                        Dim asyncResult = processor.BeginInvoke(sourceItem, callback, processor)
                    End If
                End Sub)
            If sourceDirs.Count > 0 Then
                Parallel.ForEach(sourceDirs,
                    Sub(dirItem, state)
                        If sourcePath.EndsWith("\") = False Or (sourcePath.Contains("/") = True And sourcePath.EndsWith("/") = False) Then
                            sourcePath += "\"
                        End If
                        Dim nextRalative = GetRelativePath(sourcePath, dirItem.FullName).Substring(2)
                        CheckFilesInDirectory(sourcePath, nextRalative, processor, callback, exts)
                    End Sub)
            End If
        Catch ex As Exception
            Throw ex
        End Try
    End Sub

    Public Function ConvertToString(ByVal data() As Byte) As String
        Return Encoding.GetEncoding("gb2312").GetString(data, 0, data.Length)
    End Function

    Public Function ConvertToByteArray(ByVal str As String) As Byte()
        Return Encoding.GetEncoding("gb2312").GetBytes(str)
    End Function

    Public Sub MakeZipFile(ByVal outFilePath As String, ByVal level As Integer, ByVal ParamArray files() As String)
        If files.Length > 0 Then
            Dim relativePath = GetCommonParentDir(files)
            If relativePath <> "" Then
                Using fs As New FileStream(outFilePath, FileMode.Create)
                    Using zs As New ZipOutputStream(fs)
                        zs.SetLevel(level)
                        For Each item In files
                            Call AddZipEntry(item, zs, relativePath)
                        Next
                        zs.Close()
                    End Using
                    fs.Close()
                End Using
            Else
                Throw New Exception("Failed to execute function GetCommonParentDir.")
            End If
        Else
            Throw New ArgumentException("Empty file list was given to function MakeZipFile.")
        End If
    End Sub

    Public Sub AddZipEntry(ByVal item As String, ByVal zs As ZipOutputStream, Optional ByVal relativePath As String = "")
        If System.IO.File.Exists(item) Then
            Dim z = New ZipEntry(GetRelativePath(relativePath, item).Substring(2))
            zs.PutNextEntry(z)
            Dim b = My.Computer.FileSystem.ReadAllBytes(item)
            zs.Write(b, 0, b.Length)
        ElseIf System.IO.Directory.Exists(item) Then
            Dim di As New DirectoryInfo(item)
            Dim subDirs = di.GetDirectories()
            If subDirs.Length = 0 Then
                Dim z = New ZipEntry(GetRelativePath(relativePath, item).Substring(2) & "\")
                zs.PutNextEntry(z)
            Else
                For Each subDir In di.GetDirectories()
                    Dim z = New ZipEntry(GetRelativePath(relativePath, subDir.FullName).Substring(2) & "\")
                    zs.PutNextEntry(z)
                    Call AddZipEntry(subDir.FullName, zs, relativePath)
                Next
            End If
            For Each subFile In di.GetFiles()
                Call AddZipEntry(subFile.FullName, zs, relativePath)
            Next
        End If
    End Sub

End Module